#include "doorbell_wsclient.h"
#include <esp_websocket_client.h>
#include <esp_log.h>
#include "doorbell_camera.h"
#include "doorbell_mqtt.h"
#include "doorbell_led.h"

#define TAG "doorbell_wsclient"

static const char *sound_uri = "ws://" SERVER_IP ":8057/ws/from_esp";
static const char *image_uri = "ws://" SERVER_IP ":8057/ws/image";

static esp_websocket_client_handle_t sound_client = NULL;
static esp_websocket_client_handle_t image_client = NULL;

static RingbufHandle_t mic_buffer = NULL;
static RingbufHandle_t speaker_buffer = NULL;

static TaskHandle_t sound_task_handle = NULL;
static TaskHandle_t image_task_handle = NULL;

static bool started = false;

static void doorbell_wsclient_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
    void *buf;
    switch (event_id)
    {
    case WEBSOCKET_EVENT_BEGIN:
        ESP_LOGI(TAG, "%s client begin", handler_args == sound_client ? "sound" : "image");
        break;
    case WEBSOCKET_EVENT_CONNECTED:
        ESP_LOGI(TAG, "%s client connected", handler_args == sound_client ? "sound" : "image");
        break;
    case WEBSOCKET_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "%s client disconnected", handler_args == sound_client ? "sound" : "image");
        break;
    case WEBSOCKET_EVENT_DATA:
        // 收到了服务器发来的数据包，首先判断来自于哪个客户端。来自于image client的数据，不用处理。
        if (handler_args == image_client)
        {
            break;
        }
        if (data->op_code != 0x2)
        {
            // 不是二进制数据包，不用处理
            ESP_LOGI(TAG, "sound client data opcode %d", data->op_code);
        }
        // 处理二进制数据

        assert(xRingbufferSendAcquire(speaker_buffer, &buf, data->data_len, portMAX_DELAY) == pdTRUE);
        memcpy(buf, data->data_ptr, data->data_len);
        assert(xRingbufferSendComplete(speaker_buffer, buf) == pdTRUE);

        break;
    case WEBSOCKET_EVENT_ERROR:
        ESP_LOGE(TAG, "%s client error", handler_args == sound_client ? "sound" : "image");
        break;
    case WEBSOCKET_EVENT_FINISH:
        ESP_LOGI(TAG, "%s client finish", handler_args == sound_client ? "sound" : "image");
        break;
    }
}

/** 切换wsclient的开关状态 */
void doorbell_wsclient_cb(void *arg)
{
    started = !started;
    ESP_LOGI(TAG, "switch %s", started ? "on" : "off");
    if (started)
    {
        doorbell_led_on();
        doorbell_wsclient_start();
        doorbell_mqtt_publish("{\"status\":\"on\"}", 15);
    }
    else
    {
        doorbell_led_off();
        doorbell_wsclient_stop();
        doorbell_mqtt_publish("{\"status\":\"off\"}", 16);
    }
}

void doorbell_wsclient_init(RingbufHandle_t mic_buf, RingbufHandle_t speaker_buf)
{
    esp_websocket_client_config_t config = {};
    // 初始化sound client
    config.uri = sound_uri;
    sound_client = esp_websocket_client_init(&config);
    assert(sound_client);
    ESP_ERROR_CHECK(esp_websocket_register_events(sound_client, WEBSOCKET_EVENT_ANY, doorbell_wsclient_event_handler, (void *)sound_client));

    // 初始化image client
    config.uri = image_uri;
    image_client = esp_websocket_client_init(&config);
    assert(image_client);
    ESP_ERROR_CHECK(esp_websocket_register_events(image_client, WEBSOCKET_EVENT_ANY, doorbell_wsclient_event_handler, (void *)image_client));

    // 初始化缓冲区指针
    mic_buffer = mic_buf;
    speaker_buffer = speaker_buf;

    // 注册mqtt回调函数
    doorbell_mqtt_cmd_cb cmd_cb = {
        .cmd = "switch",
        .cb = doorbell_wsclient_cb,
        .arg = NULL,
    };
    doorbell_mqtt_register_callback(&cmd_cb);
}

/** 将mic数据发送到sound client */
void doorbell_wsclient_sound_task(void *arg)
{
    void *buf = NULL;
    size_t len = 0;
    while (started)
    {
        buf = xRingbufferReceive(mic_buffer, &len, portMAX_DELAY);
        if (buf)
        {
            if (esp_websocket_client_is_connected(sound_client))
            {
                esp_websocket_client_send_bin(sound_client, (char *)buf, (int)len, portMAX_DELAY);
            }

            vRingbufferReturnItem(mic_buffer, buf);
        }
    }
    vTaskDelete(NULL);
}

/** 将image数据发送到image client */
void doorbell_wsclient_image_task(void *arg)
{
    doorbell_camera_frame_t frame;
    while (started)
    {
        if (doorbell_camera_get_frame(&frame) == ESP_OK)
        {
            if (esp_websocket_client_is_connected(image_client))
            {
                esp_websocket_client_send_bin(image_client, (char *)frame.data, (int)frame.size, portMAX_DELAY);
            }

            doorbell_camera_free(&frame);
        }
    }
    vTaskDelete(NULL);
}

void doorbell_wsclient_start(void)
{
    esp_websocket_client_start(sound_client);
    esp_websocket_client_start(image_client);
    xTaskCreate(doorbell_wsclient_sound_task, "ws_sound_task", 4096, NULL, 5, &sound_task_handle);
    xTaskCreate(doorbell_wsclient_image_task, "ws_image_task", 4096, NULL, 5, &image_task_handle);
}

void doorbell_wsclient_stop(void)
{
    // vTaskDelete(sound_task_handle);
    // vTaskDelete(image_task_handle);
    esp_websocket_client_close(sound_client, portMAX_DELAY);
    esp_websocket_client_close(image_client, portMAX_DELAY);
}

void doorbell_wsclient_deinit(void)
{
    esp_websocket_client_destroy(sound_client);
    esp_websocket_client_destroy(image_client);
}
